﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tycho.Parser.Peg;

namespace Tycho.Grammar
{
    public class GrammarLoader
    {
        public static IProduction CreateSyntax (IProduction outerProduction) {
            var everything = new ChoiceProduction ();
            var singles = new ChoiceProduction (CreateCapture (outerProduction), CreateSubRule (everything), CreateKeyword (), IdentifierProduction.CreateTerminal ());
            var multiples = new ChoiceProduction (CreateZeroOrMore (singles), CreateOneOrMore (singles), CreateOptional (singles), CreateDelimited (singles), singles);
            var sequence = new ChoiceProduction (CreateSequence (multiples), multiples);
            everything.Productions = new[] {CreateChoice (sequence), sequence};

            return everything;
        }

        public static IProduction CreateCapture (IProduction innerProduction) {
            var nameCapture = new NamedRule ("name", IdentifierProduction.CreateTerminal ());
            var productionCapture = new NamedRule ("production", innerProduction);

            return new NonTerminal ("capture", new SequenceRule (nameCapture, new KeywordRule (":"), productionCapture));
        }

        public static IProduction CreateOptional (IProduction innerProduction) {
            var op = new KeywordRule ("?");
            var rule = new NamedRule ("rule", innerProduction);
            var seq = new SequenceRule (rule, op);
            return new NonTerminal ("optional", seq);
        }

        public static IProduction CreateSubRule (IProduction innerProduction) {
            var leftBracket = new KeywordRule ("(");
            var rightBracket = new KeywordRule (")");
            var innerRule = new NamedRule ("rule", innerProduction);
            return new NonTerminal ("sub-rule", new SequenceRule (leftBracket, innerRule, rightBracket));
        }

        public static IProduction CreateChoice (IProduction production) {
            var choiceOperator = new KeywordRule ("/");
            var choice = new NamedRule ("choices", production);
            var choices = new SequenceRule (choice, new MultipleRule (1, -1, new SequenceRule (choiceOperator, choice)));
            return new NonTerminal ("choice", choices, new [] {"choices"});
        }

        public static IProduction CreateZeroOrMore (IProduction production) {
            var op = new KeywordRule ("*");
            var rule = new NamedRule ("rule", production);
            var seq = new SequenceRule (rule, op);
            return new NonTerminal ("zero-or-more", seq);
        }

        public static IProduction CreateOneOrMore (IProduction production) {
            var op = new KeywordRule ("+");
            var rule = new NamedRule ("rule", production);
            var seq = new SequenceRule (rule, op);
            return new NonTerminal ("one-or-more", seq);
        }

        public static IProduction CreateSequence (IProduction production) {
            var rules = new NamedRule ("rules", production);
            return new NonTerminal ("sequence", new MultipleRule (2, -1, rules), new[] {"rules"});
        }

        public static IProduction CreateKeyword () {
            var str = new NamedRule ("name", StringProduction.CreateTerminal ());
            return new NonTerminal ("keyword", str);
        }

        public static IProduction CreateDelimited (IProduction production) {
            var rule = new NamedRule ("rule", production);
            var delimiter = new NamedRule ("delimiter", production);
            var etc = new KeywordRule ("...");
            var seq = new SequenceRule (rule, delimiter, etc);
            return new NonTerminal ("delimited", seq);
        }

        public static IProduction CreateProduction () {
            var everything = new ChoiceProduction (null);

            everything.Productions = new [] {
                CreateDynamicChoice (everything),
                CreateTermMacro (everything),
                CreateNonTerminal (everything),
                CreateAnonymousNonTerminal (everything),
                CreateString (everything),
                IdentifierProduction.CreateTerminal (),
                CreateQuotedExpression (IdentifierProduction.CreateTerminal ()),
            };

            return everything;
        }

        public static IProduction CreateAnonymousNonTerminal (IProduction everything) {
            var openParen = new KeywordRule ("(");
            var rules = new NamedRule ("rule", CreateSyntax (everything));
            var closeParen = new KeywordRule (")");
            var seq = new SequenceRule (openParen, rules, closeParen);
            return new NonTerminal ("anonymous-non-terminal", seq);
        }

        public static IProduction CreateNonTerminal (IProduction outerProduction) {
            var name = new NamedRule ("name", IdentifierProduction.CreateTerminal ());
            var openParen = new KeywordRule ("{");
            var rules = new NamedRule ("rule", CreateSyntax (outerProduction));
            var closeParen = new KeywordRule ("}");
            var seq = new SequenceRule (name, openParen, rules, closeParen);
            return new NonTerminal ("non-terminal", seq);
        }

        public static IProduction CreateIdentifier () {
            return new NonTerminal("identifier", new KeywordRule("identifier"));
        }

        public static IProduction CreateInteger () {
            return new NonTerminal("integer", new KeywordRule("integer"));
        }

        public static IProduction CreateReal () {
            return new NonTerminal("real", new KeywordRule("real"));
        }

        public static IProduction CreateString (IProduction interpolatedProduction) {
            var interpolatedRule = new NamedRule ("interpolation-production", interpolatedProduction);
            return new NonTerminal("string", new SequenceRule(new KeywordRule ("string"), new OptionalRule (interpolatedRule)));
        }

        public static IProduction CreateQuotedExpression (IProduction production) {
            var expression = new NamedRule ("expression", production);
            return new NonTerminal ("quote", new SequenceRule (new KeywordRule ("#"), expression));
        }

        public static IProduction CreateGrammar () {
            IProduction unnamedProduction = CreateProduction ();

            var namedProductionRule = new SequenceRule (new NamedRule ("name", IdentifierProduction.CreateTerminal ()),
                                         new KeywordRule ("="),
                                         new NamedRule ("production", unnamedProduction));

            var namedProduction = new NonTerminal ("named-production",
                                                   namedProductionRule);

            var production = new ChoiceProduction (namedProduction, unnamedProduction);

            var namedProductions = new DelimitedRule (
                new NamedRule ("productions", production),
                new KeywordRule (";"));

            return new NonTerminal ("grammar", namedProductions, new [] {"productions"});
        }

        public static IProduction CreateDynamicChoice (IProduction anyProduction) {
            var delimiter = new ChoiceRule (new KeywordRule (";"), new KeywordRule (","));
            var choiceKeyword = new KeywordRule ("choice");
            var openBrace = new KeywordRule ("{");
            var closeBrace = new KeywordRule ("}");
            var namedProduction = new NonTerminal ("named-production",
                                                   new SequenceRule (
                                                       new NamedRule ("name", IdentifierProduction.CreateTerminal ()),
                                                       new KeywordRule ("="),
                                                       new NamedRule ("production", anyProduction)));
            var namedUnnamed = new ChoiceProduction (namedProduction, anyProduction);
            var choices = new NamedRule("choices", namedUnnamed);
            var delimitedChoices = new DelimitedRule (choices, delimiter);
            var seq = new SequenceRule (choiceKeyword, openBrace, delimitedChoices, closeBrace);

            return new NonTerminal ("dynamic-choice", seq, new [] {"choices"});
        }

        public static IProduction CreateTermMacro (IProduction outerProduction) {
            var macroKeyword = new KeywordRule ("macro");
            var openParen = new KeywordRule ("{");
            var closeParen = new KeywordRule ("}");
            var rules = new NamedRule ("rule", CreateSyntax (outerProduction));
            var arrow = new KeywordRule ("=>");
            var termKeyword = new KeywordRule ("term");

            IProduction termNonTerminal = new TermGrammarBuilder ().Production;
            var term = new NamedRule ("term", termNonTerminal);
            return new NonTerminal("term-macro", new SequenceRule (macroKeyword, openParen, rules, closeParen, arrow, termKeyword, term));
        }
    }
}
